﻿using OA.Infrastructure.Extensions;

namespace AMS.Services.Impl;

public class FreeAssetsStorageService : IFreeAssetsStorageService
{
    private readonly DbContext _context;

    private readonly IApprovalService _approvalService;

    public FreeAssetsStorageService(DbContext context, IApprovalService approvalService)
    {
        _context = context;

        _approvalService = approvalService;
    }

    public async Task<PageResult<FreeAssetsStorage.QueryModel>> GetPageAsync(FreeAssetsStorage.ConditionSelectorCondition condition, CancellationToken cancellationToken)
    {
        var result = new PageResult<FreeAssetsStorage.QueryModel>();

        try
        {
            var expression = GetConditionSelectorCondition(condition);

            var freeAssetsStorages = await _context.Set<FreeAssetsStorage>()
                .Include(x => x.Category)
                .Include(x => x.Supplier)
                .Include(x => x.Commiter).ThenInclude(x => x.Department)
                .Include(x => x.Commiter).ThenInclude(x => x.DirectLeader)
                .Include(x => x.Commiter).ThenInclude(x => x.Roles)
                .Where(expression)
                .OrderByDescending(x => x.CreatedDate)
                .Skip((condition.PageNum - 1) * condition.PageSize)
                .Take(condition.PageSize)
                .AsNoTracking()
                .ToListAsync(cancellationToken);

            result.Data = freeAssetsStorages.Select(x => GetQueryModel(x));

            result.PageNum = condition.PageNum;

            result.PageSize = condition.PageSize;

            result.Total = await _context.Set<FreeAssetsStorage>().CountAsync(expression, cancellationToken);

            result.Status = OperationalResult.SUCCESS;
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    public async Task<OperationalResult> CreateAsync(FreeAssetsStorage.CreateModel freeAssetsStorage, CancellationToken cancellationToken)
    {
        var result = new OperationalResult();

        var currentUser = _context.CurrentUser();

        try
        {
            var entity = new FreeAssetsStorage
            {
                Commiter = currentUser,
                Name = freeAssetsStorage.Name,
                Batch = freeAssetsStorage.Batch,
                Count = freeAssetsStorage.Count,
                Univalent = freeAssetsStorage.Univalent,
                Description = freeAssetsStorage.Description,
                Remark = freeAssetsStorage.Remark,
            };

            var entityCategory = await _context.Set<AssetsCategory>().FindAsync(new object[] { freeAssetsStorage.CategoryId }, cancellationToken);

            if (entityCategory is not null)
            {
                entity.Category = entityCategory;
            }

            var entitySupplier = await _context.Set<Supplier>().FindAsync(new object[] { freeAssetsStorage.SupplierId }, cancellationToken);

            if (entitySupplier is not null)
            {
                entity.Supplier = entitySupplier;
            }

            await _context.Set<FreeAssetsStorage>().AddAsync(entity, cancellationToken);

            var affectedRows = await _context.SaveChangesAsync(cancellationToken);

            result.Status = affectedRows > 0 ? OperationalResult.SUCCESS : OperationalResult.FAILURE;
            result.Message = $"{affectedRows} rows affected.";

            if (result.Status == OperationalResult.SUCCESS)
            {
                _ = await _approvalService.CreateApprovalAsync(currentUser, entity.Id, ApprovalType.Storage, AssetsType.Consumable, freeAssetsStorage.Remark, ApprovalService.freeAssetsStorageApprovalFlow, cancellationToken);
            }
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    private static FreeAssetsStorage.QueryModel GetQueryModel(FreeAssetsStorage freeAssetsStorage)
    {
        return new FreeAssetsStorage.QueryModel
        {
            Id = freeAssetsStorage.Id,
            CreatedDate = freeAssetsStorage.CreatedDate,
            LastModifiedDate = freeAssetsStorage.LastModifiedDate,
            Name = freeAssetsStorage.Name,
            Category = AssetsCategoryService.GetQueryModel(freeAssetsStorage.Category),
            Supplier = SupplierService.GetQueryModel(freeAssetsStorage.Supplier),
            Batch = freeAssetsStorage.Batch,
            Count = freeAssetsStorage.Count,
            Univalent = freeAssetsStorage.Univalent,
            Description = freeAssetsStorage.Description,
            Remark = freeAssetsStorage.Remark,
            Commiter = DefinitionServiceExtensions.GetUserQueryModel(freeAssetsStorage.Commiter),
            Status = freeAssetsStorage.Status,
        };
    }

    private static Expression<Func<FreeAssetsStorage, bool>> GetConditionSelectorCondition(FreeAssetsStorage.ConditionSelectorCondition condition)
    {
        Expression<Func<FreeAssetsStorage, bool>>? expression = null;

        if (condition.Keywords is not null)
        {
            Expression<Func<FreeAssetsStorage, bool>>? keywords = null;
            keywords = freeAssetsStorage => freeAssetsStorage.Name.Contains(condition.Keywords);
            keywords = keywords.Or(freeAssetsStorage => freeAssetsStorage.Supplier.Name.Contains(condition.Keywords));
            keywords = keywords.Or(freeAssetsStorage => freeAssetsStorage.Batch.Contains(condition.Keywords));
            keywords = keywords.Or(freeAssetsStorage => freeAssetsStorage.Commiter.JobNumber.Contains(condition.Keywords));
            keywords = keywords.Or(freeAssetsStorage => freeAssetsStorage.Commiter.Name.Contains(condition.Keywords));
            keywords = keywords.Or(freeAssetsStorage => freeAssetsStorage.Commiter.Department.Name.Contains(condition.Keywords));
            expression = expression?.And(keywords) ?? keywords;
        }

        if (condition.StatusSelector is not null)
        {
            Expression<Func<FreeAssetsStorage, bool>>? statusSelector = null;
            statusSelector = freeAssetsStorage => condition.StatusSelector == freeAssetsStorage.Status;
            expression = expression?.And(statusSelector) ?? statusSelector;
        }

        return expression ?? (freeAssetsStorage => true);
    }
}